package butines.game;

import java.awt.Graphics2D;
import java.util.Comparator;

import butines.core.math2d.Vector2D;
import butines.core.math2d.Vector2DUtils;
import butines.game.steering.LocalSpace;
import butines.game.steering.Obstacle;
import butines.game.steering.SteerLibrary;
import butines.game.steering.Transformation;
import butines.game.steering.Vehicle;

public abstract class AbstractGameObject implements GameObject, LocalSpace, Obstacle {

	private static int NEXT_UID = 1;
		
	private int id = NEXT_UID++;
	private boolean alive = true;
	private boolean solid = true;
	private boolean selected;
	private boolean mouseOver;
	private float radius = 12f;
	private float viewDistance = 32f;
	private Vector2D heading = new Vector2D();
	private Vector2D position = new Vector2D();
	private Vector2D forward  = new Vector2D(0, 1);	
	private Vector2D side     = new Vector2D(-1, 0);
	
	public AbstractGameObject() {
	}
	
	// Implements GameObject //
	public int getId() {
		return id;
	}
	
	public boolean handleMessage(Message message) {
		return false;
	}
	
	public void update(float tpf) {
		
	}

	public void draw(Graphics2D g2d) {
		
	}
	
	public void release() {
		
	}
	
	///////////////////////////
	

	public float getRadius() {
		return radius;
	}
	
	public void setRadius(float radius) {
		if (radius <= 0) {
			throw new IllegalArgumentException("radius <= 0");
		}
		this.radius = radius;
	}
	
	public float getViewDistance() {
		return viewDistance;
	}

	public void setViewDistance(float viewDistance) {
		if (viewDistance <= 0) {
			throw new IllegalArgumentException("viewDistance <= 0");
		}
		this.viewDistance = viewDistance;
	}
	
	public boolean isAlive() {
		return alive;
	}

	public void setAlive(boolean alive) {
		this.alive = alive;
	}
	
	public boolean isSolid() {
		return solid;
	}

	public void setSolid(boolean solid) {
		this.solid = solid;
	}
	
	public boolean isSelected() {
		return selected;
	}

	public void setSelected(boolean selected) {
		this.selected = selected;
	}
	
	public boolean isMouseOver() {
		return mouseOver;
	}

	public void setMouseOver(boolean mouseOver) {
		this.mouseOver = mouseOver;
	}

	public float distance(AbstractGameObject entity) {
		if (entity == null) {
			throw new IllegalArgumentException("entity == null");
		}
		return Vector2DUtils.distance(position, entity.position);
	}
	
	// implements LocalSpace //
	private Transformation trans;
	
	private Transformation trans() {
		if (trans == null) {
			trans = new Transformation();			
		}
		trans.setForward(forward);
		trans.setPosition(position);
		trans.setSide(side);
		return trans;
	}
	
	public Vector2D getPosition() {
		return position;
	}

	public void setPosition(float x, float y) {
		getPosition().set(x, y);
	}
	
	public Vector2D getSide() {
		return side;
	}

	public Vector2D getForward() {
		return forward;
	}
	
	public Vector2D getHeading() {
		return heading;
	}

	public void setHeading(Vector2D heading) {
		this.heading.set(heading);
	}

	public Vector2D toLocalPoint(Vector2D globalPoint) {
		return trans().toLocalPoint(globalPoint);
	}

	public Vector2D toLocalVector(Vector2D globalVector) {
		return trans().toLocalVector(globalVector);
	}

	public Vector2D toGlobalVector(Vector2D localVector) {
		return trans().toGlobalVector(localVector);
	}

	public Vector2D toGlobalPoint( Vector2D localPoint) {
		return trans().toGlobalPoint(localPoint);
	}
	
	// implements Obstacles //
	public Intersection intersectionWith(Vehicle vehicle) {
		return SteerLibrary.intersectionWith(vehicle, this, getPosition(), getRadius());
	}

	public Vector2D steerToAvoid(Vehicle vehicle) {
		return SteerLibrary.steerToAvoid(vehicle, getPosition(), getRadius());
	}

	public boolean collidedWith(Vehicle vehicle) {
		if (vehicle == this || vehicle == null) {
			return false;
		}
		float minDistance = vehicle.getRadius() + getRadius();
		return SteerLibrary.collided(getPosition(), vehicle.getPosition(), minDistance);
	}
	
	public static final Comparator<AbstractGameObject> DRAW_ORDER = new Comparator<AbstractGameObject>() {
		public int compare(AbstractGameObject a, AbstractGameObject b) {
			return Math.round(a.getPosition().y - b.getPosition().y);
		}
	};
	
}
